package test.enba.redis.controller;

import com.alibaba.fastjson.TypeReference;
import com.enba.boot.redis.RedisOperator;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/redis")
public class RedisController {

  @Autowired private RedisOperator redisOperator;

  // 字符串类型操作

  /**
   * 设置指定key的字符串值
   *
   * @param key 键
   * @param value 值
   */
  @GetMapping("/set")
  public void set(String key, String value) {
    redisOperator.set(key, new RedisUser());
  }

  /**
   * 设置指定key的字符串值，并设置过期时间
   *
   * @param key 键
   * @param value 值
   * @param timeout 过期时间（秒）
   */
  @GetMapping("/set2")
  public void set2(String key, String value, long timeout) {
    redisOperator.set(key, value, timeout);
  }

  /**
   * 获取指定key的字符串值
   *
   * @param key 键
   * @return 值，如果不存在则返回null
   */
  @GetMapping("/get")
  public Object get(String key) {
    return redisOperator.get(key);
  }

  /**
   * 获取指定key的字符串值，并转换为指定类型
   *
   * @param key 键
   * @return 目标对象，如果不存在则返回null
   */
  @GetMapping("/get2")
  public RedisUser get2(String key) {
    return redisOperator.get(key, RedisUser.class);
  }

  /**
   * 获取指定key的字符串值，并转换为指定类型
   *
   * @param key 键
   * @param type 目标类型
   * @param <T> 目标对象
   * @return 目标对象，如果不存在则返回null
   */
  @GetMapping("/get3")
  public <T> T get3(String key, TypeReference<T> type) {
    return redisOperator.get(key, type);
  }

  /**
   * 删除指定的key
   *
   * @param key 键
   */
  @GetMapping("/delete")
  public void delete(String key) {
    redisOperator.delete(key);
  }

  // 列表类型操作

  /**
   * 向列表头部添加元素
   *
   * @param key 列表的键
   * @param value 要添加的元素
   * @return 列表长度
   */
  @GetMapping("/lpush")
  public Long lPush(String key, @RequestParam("value") Object value) {
    return redisOperator.lPush(key, value);
  }

  /**
   * 从列表头部获取元素 如果你使用的 Redis 服务器版本低于 6.2.0，调用 leftPop(key, count) 会报错
   *
   * @param key 列表的键
   * @param count 要弹出的元素个数
   * @return 从列表头部弹出的元素
   */
  @GetMapping("/lpop")
  public Object lPop(String key, long count) {
    return redisOperator.lPop(key);
  }

  /**
   * 向列表头部批量添加元素
   *
   * @param key 列表的键
   * @return 列表长度
   */
  @GetMapping("/lpushall")
  public Long lPushAll(String key) {
    return redisOperator.lPushAll(
        key, new RedisUser(), new RedisUser(), new RedisUser(), new RedisUser(), new RedisUser());
  }

  /**
   * 向列表尾部添加元素
   *
   * @param key 列表的键
   * @param value 要添加的元素
   * @return 列表长度
   */
  @GetMapping("/rpush")
  public Long rPush(String key, @RequestParam("value") Object value) {
    return redisOperator.rPush(key, value);
  }

  /**
   * 从列表尾部获取元素 如果你使用的 Redis 服务器版本低于 6.2.0，调用 rightPop(key, count) 会报错
   *
   * @param key 列表的键
   * @param count 要弹出的元素个数
   * @return 从列表头部弹出的元素
   */
  @GetMapping("/rpop")
  public Object rPop(String key, long count) {
    return redisOperator.rPop(key);
  }

  /**
   * 获取列表指定范围内的元素
   *
   * @param key 列表的键
   * @param start 起始索引（包含），0表示第一个元素
   * @param end 结束索引（包含），-1表示最后一个元素
   * @return 元素列表
   */
  @GetMapping("/lrange")
  public List<Object> lRange(String key, long start, long end) {
    return redisOperator.lRange(key, start, end);
  }

  /**
   * 删除列表中指定个数的指定元素
   *
   * @param key 列表的键
   * @param count 要删除的个数，正数表示从头部开始匹配删除，负数表示从尾部开始匹配删除，0表示删除所有匹配元素
   * @param value 要删除的元素值
   * @return 删除的元素个数
   */
  @GetMapping("/lrem")
  public Long lRem(String key, long count, @RequestParam("value") Object value) {
    return redisOperator.lRem(key, count, value);
  }

  // 集合类型操作

  /**
   * 向集合中添加元素,演示相同元素不会添加
   *
   * @param key 集合的键
   * @return 添加成功的元素个数
   */
  @GetMapping("/sadd")
  public Long sAdd(String key) {
    return redisOperator.sAdd(
        key, new RedisUser(), new RedisUser(), new RedisUser(), new RedisUser(), new RedisUser());
  }

  @GetMapping("/sadd2")
  public Long sAdd2(String key, @RequestParam("value") Object value) {
    return redisOperator.sAdd(key, value);
  }

  /**
   * 获取集合中的所有元素
   *
   * @param key 集合的键
   * @return 元素集合
   */
  @GetMapping("/smembers")
  public Set<Object> sMembers(String key) {
    return redisOperator.sMembers(key);
  }

  /**
   * 判断元素是否在集合中
   *
   * @param key 集合的键
   * @param value 要判断的元素
   * @return true表示在集合中，false表示不在
   */
  @GetMapping("/sismember")
  public Boolean sIsMember(String key, @RequestParam("value") Object value) {
    return redisOperator.sIsMember(key, value);
  }

  /**
   * 从集合中删除元素
   *
   * @param key 集合的键
   * @param values 要删除的元素集合
   * @return 删除成功的元素个数
   */
  @GetMapping("/srem")
  public Long sRem(String key, @RequestParam("value") Object... values) {
    return redisOperator.sRem(key, values);
  }

  // 有序集合类型操作

  /**
   * 向有序集合中添加元素及对应的分数
   *
   * @param key 有序集合的键
   * @param score 元素的分数
   * @param value 要添加的元素
   */
  @GetMapping("/zadd")
  public void zAdd(String key, double score, @RequestParam("value") Object value) {
    redisOperator.zAdd(key, score, value);
  }

  /**
   * 获取有序集合中指定分数区间内的元素
   *
   * @param key 有序集合的键
   * @param min 最小分数，包含，可使用"-inf"表示负无穷
   * @param max 最大分数，包含，可使用"+inf"表示正无穷
   * @return 元素集合
   */
  @GetMapping("/zrangebyscore")
  public Set<Object> zRangeByScore(String key, double min, double max) {
    return redisOperator.zRangeByScore(key, min, max);
  }

  /**
   * 获取有序集合中指定排名区间内的元素
   *
   * @param key 有序集合的键
   * @param start 起始排名（包含），0表示第一个元素
   * @param end 结束排名（包含）
   * @return 元素集合
   */
  @GetMapping("/zrange")
  public Set<Object> zRange(int start, int end, String key) {
    return redisOperator.zRange(start, end, key);
  }

  /**
   * 获取有序集合中元素的分数
   *
   * @param key 有序集合的键
   * @param value 要获取分数的元素
   * @return 元素的分数，如果元素不存在则返回null
   */
  @GetMapping("/zscore")
  public Double zScore(String key, @RequestParam("value") Object value) {
    return redisOperator.zScore(key, value);
  }

  /**
   * 删除有序集合中的元素
   *
   * @param key 有序集合的键
   * @param values 要删除的元素集合
   * @return 删除成功的元素个数
   */
  @GetMapping("/zrem")
  public Long zRem(String key, @RequestParam("value") Object... values) {
    return redisOperator.zRem(key, values);
  }

  // 哈希类型操作

  /**
   * 向哈希表中添加字段及对应的值
   *
   * @param key 哈希表的键
   * @param hashKey 字段名
   * @param value 字段对应的值
   */
  @GetMapping("/hput")
  public void hPut(String key, String hashKey, @RequestParam("value") Object value) {
    redisOperator.hPut(key, hashKey, value);
  }

  /**
   * 向哈希表中批量添加字段及对应的值
   *
   * @param key 哈希表的键
   */
  @GetMapping("/hputall")
  public void hPutAll(String key) {
    // 字段及对应值的映射表
    Map<String, Object> map = new HashMap<>();
    map.put("key2", "222222");
    map.put("key3", "333333");
    redisOperator.hPutAll(key, map);
  }

  /**
   * 获取哈希表中指定字段的值
   *
   * @param key 哈希表的键
   * @param hashKey 字段名
   * @return 字段对应的值，如果字段不存在则返回null
   */
  @GetMapping("/hget")
  public Object hGet(String key, String hashKey) {
    return redisOperator.hGet(key, hashKey);
  }

  /**
   * 获取哈希表中所有的字段及对应的值
   *
   * @param key 哈希表的键
   * @return 字段及对应值的映射表
   */
  @GetMapping("/hgetall")
  public Map<Object, Object> hGetAll(String key) {
    return redisOperator.hGetAll(key);
  }

  /**
   * 删除哈希表中的指定字段
   *
   * @param key 哈希表的键
   * @param hashKeys 要删除的字段集合
   * @return 删除成功的字段个数
   */
  @GetMapping("/hdel")
  public Long hDel(String key, @RequestParam("value") Object... hashKeys) {
    return redisOperator.hDel(key, hashKeys);
  }

  /**
   * 设置指定key的过期时间（以秒为单位）
   *
   * @param key 要设置过期时间的键
   * @param expireTime 过期时间，以秒为单位
   * @return 如果操作成功返回true，否则返回false
   */
  @GetMapping("/setexpireforkey")
  public boolean setExpireForKey(String key, long expireTime) {
    return Boolean.TRUE.equals(redisOperator.setExpireForKey(key, expireTime));
  }

  /**
   * 设置指定key的过期时间（以毫秒为单位）
   *
   * @param key 要设置过期时间的键
   * @param expireTimeMs 过期时间，以毫秒为单位
   * @return 如果操作成功返回true，否则返回false
   */
  @GetMapping("/setexpireforkeyinmillis")
  public boolean setExpireForKeyInMillis(String key, long expireTimeMs) {
    return Boolean.TRUE.equals(redisOperator.setExpireForKeyInMillis(key, expireTimeMs));
  }

  /**
   * 设置指定key的过期时间到给定的日期
   *
   * @param key 要设置过期时间的键
   * @return 如果操作成功返回true，否则返回false
   */
  @GetMapping("/setexpireatdate")
  public boolean setExpireAtDate(String key) {
    // 创建一个Date实例
    Date currentDate = new Date();
    System.out.println("当前日期: " + currentDate);

    // 创建并配置Calendar实例
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(currentDate);

    // 添加1天
    calendar.add(Calendar.DAY_OF_MONTH, 1);
    Date tomorrow = calendar.getTime();

    return Boolean.TRUE.equals(redisOperator.setExpireAtDate(key, tomorrow));
  }

  /**
   * 尝试获取分布式锁
   *
   * @param lockKey 锁的键
   * @param requestId 请求ID
   * @param expireTime 过期时间，单位为秒
   * @return 是否获取到锁
   */
  @GetMapping("/trylock")
  public boolean tryLock(String lockKey, String requestId, long expireTime) {
    return redisOperator.tryLock(lockKey, requestId, expireTime);
  }

  /**
   * 释放锁
   *
   * @param lockKey 锁的键
   * @param requestId 请求ID
   */
  @GetMapping("/unlock")
  public void unlock(String lockKey, String requestId) {
    redisOperator.unlock(lockKey, requestId);
  }

  /**
   * 使用分布式锁执行给定的回调函数，如果获取锁成功，则执行回调函数并返回结果，执行完后自动释放锁；如果获取锁失败，则抛出异常或返回默认值（根据回调函数类型）。获取不到锁直接返回null
   *
   * @param lockKey 锁的键
   * @param expireTime 锁的过期时间（秒）
   * @return 回调函数执行的结果
   */
  @GetMapping("/executewithlocknoblock")
  public String executeWithLockNoBlock(String lockKey, long expireTime) {
    return redisOperator.executeWithLockNoBlock(
        lockKey,
        expireTime,
        () -> {
          // 加锁成功，执行业务逻辑
          System.out.println("do something");

          return "加锁成功";
        });
  }

  /**
   * 使用分布式锁执行给定的回调函数，如果获取锁成功，则执行回调函数并返回结果，执行完后自动释放锁；如果获取锁失败，则抛出异常或返回默认值（根据回调函数类型）。 获取不到锁自旋等待
   *
   * @param lockKey 锁的键
   * @param expireTime 锁的过期时间（秒）
   * @param count 自旋次数
   * @return 回调函数执行的结果
   */
  @GetMapping("/executewithlockblock")
  public String executeWithLockBlock(String lockKey, long expireTime, int count) {
    return redisOperator.executeWithLockBlock(
        lockKey,
        expireTime,
        count,
        () -> {
          // 加锁成功，执行业务逻辑
          System.out.println("do something");

          return "加锁成功";
        });
  }
}
